#!/usr/bin/env python

import sys
import os
import subprocess

teststr = """string"""

bufSize = 100000

def log(str):
#    sys.stderr.write('%s: %s\n'%( __name__, str))
#    sys.stderr.flush()
    pass

class invokeException:
    pass

class forwarder:
    def __init__(self, toUp, fromUp):
        self.toUp = toUp
        self.fromUp = fromUp
        self.toDown = None
        self.fromDown = None

    def _isTerminator(self):
        return not self.toDown
        
    def invoke(self, hosts):
        if len(hosts) == 0:
            return 'S'
        try:
            self.toDown, self.fromDown =  invokeChild(hosts[0])
            self._writeDown('invoke ' + " ".join(hosts[1:]))
            ack = self._readDown()
            return ack
        except invokeException:
            log('failed to invoke on %s: skip' % hosts[0])
            return self.invoke(hosts[1:])
        
    def file(self, name, size, inStream):
        if not self._isTerminator():
            self._writeDown('file %s %d' % (name, size))
        f = file('/tmp/' + name, 'w')
        self._tee(f, inStream, size)
        if not self._isTerminator():
            ack = self._readDown()
        else:
            ack = 'S'
        return ack

    def quit(self):
        if not self._isTerminator():
            self._writeDown('quit')
            ack = self._readDown()
        else:
            ack = 'S'
        return ack

    def _tee(self, f, inStream, size):
        read = 0L
        while size > read:
            buf = inStream.read(min(bufSize, size - read))
            if len(buf) == 0:
                return
            f.write(buf)
            if not self._isTerminator():
                self.toDown.write(buf)
            read += len(buf)
        f.flush()
        if not self._isTerminator():
            self.toDown.flush()

    def start(self):
        while True:
            line = self._readUp()
            log("--" + line)
            tokens = line.strip().split()
            if tokens[0] == 'invoke':
                ack = self.invoke(tokens[1:])
                self._writeUp(ack)
            if tokens[0] == 'file':
                name = tokens[1]
                size = long(tokens[2])
                ack = self.file(name, size, self.fromUp)
                self._writeUp(ack)
            if tokens[0] == 'quit':
                ack = self.quit()
                self._writeUp(ack)
                sys.exit(0)
            
    def _writeUp(self, str):
#        log('writeUp: ' + str)
        self.toUp.write(str)
        self.toUp.write('\n')
        self.toUp.flush()

    def _readUp(self):
        return self.fromUp.readline().strip()

    def _writeDown(self, str):
        self.toDown.write(str)
        self.toDown.write('\n')
        self.toDown.flush()

    def _readDown(self):
        tmp =  self.fromDown.readline().strip()
        log('readDown: ' + tmp)
        return tmp

def escapeTripleQuote(str):
    str = str.replace('\\', '\\'+ '\\')
    return str.replace('"""', '\\' + '"' + '\\' + '"' + '\\' + '"')



def getSelf():
    if globals().has_key('__self__'):
        return __self__
    tmp = ""
    for line in file(sys.argv[0]):
        tmp += line
    return tmp

def sendSelf(stream):
    lines = []
    lines.append('__self__=""' + '"')
    lines.append(escapeTripleQuote(getSelf()))
    lines.append('"' + '""\n')
    for l in lines:
        stream.write(l)
#    stream.write("file('/tmp/tout','w').write(__self__)\n")     
    stream.write('exec(__self__)\n')
    stream.write('forwarder(sys.stdout, sys.stdin).start()\n')
    stream.flush()

def invokeChild(host):
    p = subprocess.Popen(["ssh", "-o", "StrictHostKeyChecking=no", host, "python -i" ], 
                         stdin = subprocess.PIPE,
                        stdout = subprocess.PIPE,
                         stderr = subprocess.PIPE) 
    p.stdin.write("print 'hello'\n")
    p.stdout.readline()
    if p.poll():
        raise invokeException()
    p.stdin.write("__name__ = '%s'\n" % host.split('.')[0])
    sendSelf(p.stdin)
    return p.stdin, p.stdout


def prepareFile(name):
    size = os.stat(name).st_size
    f = file(name, 'r')
    return(name.split('/')[-1], size, f)


def usage():
    sys.stderr.write("Usage: %s HOST [HOST ...] -files FILE [FILE ..]\n" % sys.argv[0])
    sys.exit(0)

def main():
    hosts = []
    files = []
    fileMode = False
    for arg in sys.argv:
        if arg == '-files':
            fileMode = True
            continue
        if not fileMode:
            hosts.append(arg)
        else:
            files.append(arg)
    if len(hosts) == 0 or len(files) == 0:
        usage()

    f = forwarder(None, None)
    f.invoke(hosts)
    for file in files:
        print f.file(* prepareFile(file))
    f.quit()

if __name__ == "__main__":
    main()
